import struct
from uuid import UUID
from binascii import hexlify

class Field:
    """a field model for a template

    Fields are used inside a block which in turn are inside a template. The fields
    just implement a conversion method for converting a value to a byte representation.

    Fields do not implement any type logic. This should be up to type classes which can then
    be fed into the fields (at some point in the future). Thus the logic is separated from the
    network representation.

    A Field stores a name and has a length and a method toBytes(value).

    The fields are used inside the block code in e.g. generating the resulting byte sequence
    from the block contents.

    TODO:
    Variable and Multiple fields

    """

    def __init__(self, name):
        """store variable data"""
        self.name = name

    def __len__(self):
        """return the length"""
        return struct.calcsize(self.fmt)

    def toBytes(self,value):
        """convert a value according to the format"""
        return struct.pack(self.fmt,value)

class S32Field(Field):
    """a S32 field"""
    typ="S32"
    fmt=">i"

class S16Field(Field):
    """a S16 field"""
    typ="S16"
    fmt=">h"

class S8Field(Field):
    """a S8 field"""
    typ="S8"
    fmt=">b"

class U32Field(Field):
    """a U32 field"""
    typ="U32"
    fmt=">I"

class U16Field(Field):
    """a U16 field"""
    typ="U16"
    fmt=">H"

class U8Field(Field):
    """a U8 field"""
    typ="U8"
    fmt=">B"

class LLUUIDField(Field):
    """an LLUUID field

    We assume the same API for the value as the uuid module provides which
    is mainly the bytes property
    """
    typ="LLUUID"

    def toBytes(self,value):
        """convert a UUID string"""
        return UUID(value).bytes

    def __len__(self):
        return 16

class LLVector3Field(Field):
    """a single precision 3d vector"""
    typ="LLVector3"
    fmt = ">fff"

    def toBytes(self,vector):
        """value is a 3-tuple"""
        return struct.pack(self.fmt,*vector)

class LLVector3dField(LLVector3Field):
    """a double precision 3d vector"""
    typ="LLVector3d"
    fmt = ">ddd"

class LLVector4Field(LLVector3Field):
    """a single precisition 4d vector"""
    typ="LLVector4"
    fmt = ">ffff"

class LLQuaternionField(LLVector3Field):
    """a single precision quaternion"""
    typ="LLQuaternion"
    fmt = ">ffff"

def testme():
        """

        Fields are used to model a field in a block. 

        let's try the simple unsignedfields

        >>> u8 = U8Field("foobar")
        >>> u8.name
        'foobar'
        >>> u8.toBytes(23)
        '\\x17'
        >>> len(u8)
        1

        try U16
        >>> u16 = U16Field("u16")
        >>> u16.toBytes(23)
        '\\x00\\x17'
        >>> len(u16)
        2

        try U32
        >>> u32 = U32Field("u32")
        >>> u32.toBytes(23)
        '\\x00\\x00\\x00\\x17'
        >>> len(u32)
        4

        and the signed ones
        try s8
        >>> s8 = S8Field("s8")
        >>> s8.toBytes(129)
        '\\x81'
        >>> len(s8)
        1

        try S16
        >>> s16 = S16Field("s16")
        >>> s16.toBytes(129)
        '\\x00\\x81'
        >>> len(s16)
        2

        try S32
        >>> s32 = S32Field("s32")
        >>> s32.toBytes(129)
        '\\x00\\x00\\x00\\x81'
        >>> len(s32)
        4

        check now the UUIDs
        first, create one
        >>> uuid = "35353fa3-8303-4d1b-aa08-f54416c8c921"
        >>> uuidfield = LLUUIDField("uuid")
        >>> uuidfield.name
        'uuid'
        >>> hexlify(uuidfield.toBytes(uuid))
        '35353fa383034d1baa08f54416c8c921'

        also check the length of it
        >>> len(uuidfield)
        16
        >>> len(uuidfield.toBytes(uuid))
        16


        good time to test the vectors, too. So let's first define the fields:
        >>> v3 = LLVector3Field("v3")
        >>> v3d = LLVector3dField("v3d")
        >>> v4 = LLVector4Field("v4")
        >>> vq = LLQuaternionField("vq")

        now some example data
        >>> a1=(3.32,5.627,8.1)
        >>> a2=(3.32,5.627,8.1,67.221)

        and now test these classes
        >>> hexlify(v3.toBytes(a1))
        '40547ae140b410624101999a'

        >>> hexlify(v3d.toBytes(a1))
        '400a8f5c28f5c28f4016820c49ba5e354020333333333333'

        >>> hexlify(v4.toBytes(a2))
        '40547ae140b410624101999a42867127'

        >>> hexlify(vq.toBytes(a2))
        '40547ae140b410624101999a42867127'

        also test some lengths
        >>> len(v3)
        12
        >>> len(v3d)
        24
        >>> len(v4)
        16
        >>> len(vq)
        16

        """


def _test():
    import doctest
    doctest.testmod()

if __name__=="__main__":    
    _test()
